<!--
Google IO 2012 HTML5 Slide Template

Authors: Eric Bidelman <ebidel@gmail.com>
         Luke Mahé <lukem@google.com>

URL: https://code.google.com/p/io-2012-slides
-->
<!DOCTYPE html>
<html>
<head>
  <title>Google IO 2012</title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="chrome=1">
  <!--<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">-->
  <!--<meta name="viewport" content="width=device-width, initial-scale=1.0">-->
  <!--This one seems to work all the time, but really small on ipad-->
  <!--<meta name="viewport" content="initial-scale=0.4">-->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <link rel="stylesheet" media="all" href="theme/css/default.css">
  <link rel="stylesheet" media="only screen and (max-device-width: 480px)" href="theme/css/phone.css">
  <base target="_blank"> <!-- This amazingness opens all links in a new tab. -->
  <script data-main="js/slides" src="js/require-1.0.8.min.js"></script>
</head>
<body style="opacity: 0">

<slides class="layout-widescreen">

  <slide class="logoslide nobackground">
    <article class="flexbox vcenter">
      <span><img src="images/google_developers_logo.png"></span>
    </article>
  </slide>

  <slide class="title-slide segue nobackground">
    <aside class="gdbar"><img src="images/google_developers_icon_128.png"></aside>
    <!-- The content of this hgroup is replaced programmatically through the slide_config.json. -->
    <hgroup class="auto-fadein">
      <h1 data-config-title><!-- populated from slide_config.json --></h1>
      <h2 data-config-subtitle><!-- populated from slide_config.json --></h2>
      <p data-config-presenter><!-- populated from slide_config.json --></p>
    </hgroup>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Anti-Pattern #1</h2>
      <h3>Don't do query.get()</h3>
    </hgroup>
    <article>
      {{ code("snippets/query.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #1</h2>
      <h3>When you can do get_by_id</h3>
    </hgroup>
    <article>
      {{ code("snippets/getbykey.py") }}
    </article>
  </slide>


  <slide>
    <hgroup>
      <h2>Datastore Pattern #1</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li><code>query</code> has to first scan the index table for the entity key: 2 x READ</li>
	<li><code>get_by_id</code> lookups directly from the entity table: 1 x READ</li>
	<li>Faster and cheaper</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Anti-Pattern #2</h2>
      <h3>Don't use indexed properties</h3>
    </hgroup>
    <article>
      {{ code("snippets/indexed.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #2</h2>
      <h3>When you will never need to query against them</h3>
    </hgroup>
    <article>
      {{ code("snippets/unindexed.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #2</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li>When updating an entity the datastore has to delete and insert a new index of each <code>indexed</code> property: 4 x WRITE + 1 WRITE</li>
	<li><code>indexed=False</code> will only update the entity body: 1 WRITE</li>
	<li>Faster, less bandwidth, cheaper</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Anti-Pattern #3</h2>
      <h3>Don't query full entities</h3>
    </hgroup>
    <article>
      {{ code("snippets/fullentity.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #3</h2>
      <h3>When you can do projection query</h3>
    </hgroup>
    <article>
      {{ code("snippets/projection.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #3</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li>regular queries will fetch all the entity fields: 1 x READ</li>
	<li><code>projection</code> queries will only fetch projected entity fields: 1 x SMALL READ</li>
	<li>Faster, less bandwidth, maybe cheaper (because projected properties need to be indexed)</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Anti-Pattern #4</h2>
      <h3>Don't use offset</h3>
    </hgroup>
    <article>
      {{ code("snippets/offset.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #4</h2>
      <h3>When you can use cursor</h3>
    </hgroup>
    <article>
      {{ code("snippets/cursor.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>Datastore Pattern #4</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li><code>offset=10,limit=10</code> discards the first 10 results, but still fetch them: 20 x READ</li>
	<li><code>start_cursor=cursor</code> resumes fetching at the cursor position: 10 x READ</li>
	<li>Faster, less bandwidth, cheaper</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #5</h2>
      <h3>Don't get from the datastore</h3>
    </hgroup>
    <article>
            {{ code("snippets/getbykey.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #5</h2>
      <h3>When you can get from memcache</h3>
    </hgroup>
    <article>
            {{ code("snippets/getbykey_memcache.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #5</h2>
      <h3>Or from memory using ndb builtin caching</h3>
    </hgroup>
    <article>
            {{ code("snippets/getbykey_memory.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #5</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li>reading from memcache is faster</li>
	<li>reading from memcache is cheaper</li>
	<li>reading from instance memory is free</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #6</h2>
      <h3>Don't do individual RPC</h3>
    </hgroup>
    <article>
      {{ code("snippets/nobatch.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #6</h2>
      <h3>When you can batch them</h3>
    </hgroup>
    <article>
      {{ code("snippets/batch.py") }}
    </article>
  </slide>

 <slide>
    <hgroup>
      <h2>App Engine Pattern #6</h2>
      <h3>Or let ndb batch them for you</h3>
    </hgroup>
    <article>
      {{ code("snippets/asyncbatch.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #6</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li><code>put_multi</code> and <code>put_async</code> will batch writes and run them in parallel<li>
	<li>less rpcs, less bandwitch, faster</li>
	<li>applicable to datastore, memcache, taskqueue and FTS</li>
      </ul>
    </article>
  </slide>


  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #7</h2>
      <h3>Don't perform synchronous RPC in sequence</h3>
    </hgroup>
    <article>
      {{ code("snippets/sync.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #7</h2>
      <h3>When you can do async RPC in parallel</h3>
    </hgroup>
    <article>
      {{ code("snippets/async.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #7</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
	<li>latency only depends on the slowest async rpc</li>
	<li>instances can perform other computation while waiting for I/O</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #8</h2>
      <h3>Don't do individual datastore RPC</h3>
    </hgroup>
    <article>
      {{ code("snippets/notransaction.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #8</h2>
      <h3>When you need consistency</h3>
    </hgroup>
    <article>
      {{ code("snippets/transaction.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #7</h2>
      <h3>Why transaction?</h3>
    </hgroup>
    <article>
      <ul>
	    <li>Either all or none of the datastore operation are applied</li>
	    <li>Guarantee consistency & isolation</li>
	    <li>Even work with task queue <code>Transactional=True</code></li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #9</h2>
      <h3>Don't try to update and read data in the same transaction</h3>
    </hgroup>
    <article>
      {{ code("snippets/transaction_read.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #9</h2>
      <h3>Or even delete</h3>
    </hgroup>
    <article>
      {{ code("snippets/transaction_delete.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #9</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      In a transaction:
      <ul>
	    <li>All reads reflect a consistent snapshot of the Datastore ...<li>
        <li>... at the time the transaction started</li>
        <li>⚠ NDB auto-caching ⚠</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Anti-Pattern #10</h2>
      <h3>Don't do a global query</h3>
5B    </hgroup>
    <article>
      {{ code("snippets/global_query.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #10</h2>
      <h3>When you need consistency</h3>
    </hgroup>
    <article>
      {{ code("snippets/ancestor_query.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Workaround #10</h2>
      <h3>keys_only query + get_multi</h3>
    </hgroup>
    <article>
      {{ code("snippets/key_query.py") }}
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>App Engine Pattern #10</h2>
      <h3>Why?</h3>
    </hgroup>
    <article>
      <ul>
        <li>A global query can get stalled results.</li>
        <li>Multiple run of the same query can get different results.</li>
        <li>Use ancestor query to get strong consistency.</li>
        <li>Use <code>keys_get</code> & <code>get_multi</code> to trade index consistency for write throughtput.</li>
      </ul>
    </article>
  </slide>

  <slide>
    <hgroup>
      <h2>More tricks?</h2>
    </hgroup>
    <article>
      <ul>
	<li>See <a href="http://youtu.be/zQ5_47zy4bY">Google I/O 2012 - Optimizing Your Google App Engine App</a><li>
	<li>Edge caching: better latency and less instance hours</li>
	<li>Offload offline work to task queue: reduce user facing latency</li>
	<li>Performance settings: tweak scheduler behaviour for performance or cost</li>
    <li>Contribute new pattern snippets on <a href="//code.google.com/p/appstats-tour"><code>code.google.com/p/appstats-tour</code></a></li>
      </ul>
    </article>
  </slide>

  <slide class="thank-you-slide segue nobackground">
    <aside class="gdbar right"><img src="images/google_developers_icon_128.png"></aside>
    <article class="flexbox vleft auto-fadein">
      <h2>yield Thank("You!")</h2>
    </article>
    <p class="auto-fadein" data-config-contact>
      <!-- populated from slide_config.json -->
    </p>
  </slide>

  <slide class="logoslide dark nobackground">
    <article class="flexbox vcenter">
      <span><img src="images/google_developers_logo_white.png"></span>
    </article>
  </slide>

  <slide class="backdrop"></slide>

</slides>

<script>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXXX-1']);
_gaq.push(['_trackPageview']);

(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>

<!--[if IE]>
  <script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
  <script>CFInstall.check({mode: 'overlay'});</script>
<![endif]-->
</body>
</html>
